import re
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

# 设置matplotlib以正确显示中文字符
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False


def parse_backtest_log_final(file_path):
    """
    解析最终版回测日志，严格根据pnl判断盈亏，并细分亏损类型。
    """
    try:
        with open(file_path, 'r', encoding='UTF-8') as f:
            content = f.read()
    except FileNotFoundError:
        print(f"错误：找不到文件 {file_path}")
        return None
    except Exception as e:
        print(f"读取文件时出错: {e}")
        return None

    blocks = content.split('=========\n')

    open_positions = {}
    completed_trades = []

    id_pattern = re.compile(r"├─ 仓位ID : ([\w-]+)")
    time_pattern = re.compile(r"├─ 交易时间: ([\dT:+ -]+)\[")
    open_reason_pattern = re.compile(
        r"波动率: ([\d.]+), .*volaZScore\(([\d.-]+)\).*volumeZScore\(([\d.-]+)\)"
    )
    close_reason_pattern = re.compile(r"平仓原因: (.*)")
    pnl_pattern = re.compile(r"├─ 本笔盈亏: ([-\d.]+)")
    mae_pattern = re.compile(r"MAE: ([\d.]+)%")

    for block in blocks:
        id_match = id_pattern.search(block)
        if not id_match: continue
        position_id = id_match.group(1)

        time_match = time_pattern.search(block)
        if not time_match: continue
        trade_time = pd.to_datetime(time_match.group(1).strip())

        if "[开仓做" in block:
            reason_match = open_reason_pattern.search(block)
            if reason_match:
                open_positions[position_id] = {
                    'volatility': float(reason_match.group(1)),
                    'volaZScore': float(reason_match.group(2)),
                    'volumeZScore': float(reason_match.group(3)),
                    'open_time': trade_time
                }

        elif "[平仓" in block or "[止损" in block:
            pnl_match = pnl_pattern.search(block)
            reason_match = close_reason_pattern.search(block)

            if pnl_match and reason_match and position_id in open_positions:
                trade_data = open_positions.pop(position_id)
                pnl = float(pnl_match.group(1))
                trade_data['pnl'] = pnl

                duration = (trade_time - trade_data['open_time']).total_seconds() / 3600
                trade_data['duration_hours'] = duration

                reason_text = reason_match.group(1)
                mae_match = mae_pattern.search(reason_text)
                trade_data['mae'] = float(mae_match.group(1)) if mae_match else np.nan

                # --- 核心逻辑修改：根据pnl判断，然后细分亏损原因 ---
                if pnl > 0:
                    trade_data['exit_type'] = '盈利'
                else:
                    if "止损触发" in reason_text:
                        trade_data['exit_type'] = '止损亏损'
                    else:
                        trade_data['exit_type'] = '策略平仓亏损'
                # --- 修改结束 ---

                del trade_data['open_time']
                completed_trades.append(trade_data)

    if not completed_trades:
        print("警告：未能解析出任何完整交易。")
        return pd.DataFrame()

    df = pd.DataFrame(completed_trades)
    df.dropna(subset=['mae'], inplace=True)
    return df


def analyze_losses_deep_dive(df):
    """
    对不同类型的平仓进行深度分析和可视化。
    """
    if df.empty or 'mae' not in df.columns:
        print("数据不足，无法分析。")
        return

    # --- 1. 核心统计数据按新分类展示 ---
    print("\n--- 按平仓类型分组的核心统计 ---")
    grouped_stats = df.groupby('exit_type').agg(
        交易笔数=('pnl', 'count'),
        平均盈亏=('pnl', 'mean'),
        平均持仓小时=('duration_hours', 'mean'),
        平均MAE=('mae', 'mean'),
        MAE中位数=('mae', 'median')
    ).round(2)
    print(grouped_stats)

    winning_trades_mae = df[df['exit_type'] == '盈利']['mae']
    print("\n--- 盈利交易的MAE百分位数 (止损设置参考) ---")
    if not winning_trades_mae.empty:
        print(f"为了保住80%的盈利单, 止损至少应宽于: {winning_trades_mae.quantile(0.80):.2f}%")
        print(f"为了保住95%的盈利单, 止损至少应宽于: {winning_trades_mae.quantile(0.95):.2f}%")

    # --- 2. 可视化分析 ---
    fig, axes = plt.subplots(2, 2, figsize=(20, 16))
    fig.suptitle('亏损交易深度分析：如何有效过滤？', fontsize=22, y=1.02)

    # 图1: 盈利交易所需要的“呼吸空间”
    sns.histplot(data=winning_trades_mae, ax=axes[0, 0], kde=True, bins=30, color='green')
    q80 = winning_trades_mae.quantile(0.80)
    q95 = winning_trades_mae.quantile(0.95)
    axes[0, 0].axvline(q80, color='orange', linestyle='--', linewidth=2.5, label=f'80%分位点: {q80:.2f}%')
    axes[0, 0].axvline(q95, color='red', linestyle='--', linewidth=2.5, label=f'95%分位点: {q95:.2f}%')
    axes[0, 0].set_title('盈利交易的MAE分布 (止损设置的关键)', fontsize=16)
    axes[0, 0].set_xlabel('MAE (%)')
    axes[0, 0].set_ylabel('交易次数')
    axes[0, 0].legend()
    axes[0, 0].grid(True)

    # 图2: 不同平仓类型的MAE分布对比
    sns.boxplot(data=df, x='exit_type', y='mae', ax=axes[0, 1], order=['盈利', '策略平仓亏损', '止损亏损'])
    sns.stripplot(data=df, x='exit_type', y='mae', ax=axes[0, 1], alpha=0.3, color='black', order=['盈利', '策略平仓亏损', '止损亏损'])
    axes[0, 1].set_title('不同平仓类型的MAE分布', fontsize=16)
    axes[0, 1].set_xlabel('平仓类型')
    axes[0, 1].set_ylabel('MAE (%)')
    axes[0, 1].grid(axis='y')

    # 图3: 不同平仓类型的最终盈亏分布
    sns.boxplot(data=df, x='exit_type', y='pnl', ax=axes[1, 0], order=['盈利', '策略平仓亏损', '止损亏损'])
    axes[1, 0].axhline(0, color='grey', linestyle='--')
    axes[1, 0].set_title('不同平仓类型的PNL分布', fontsize=16)
    axes[1, 0].set_xlabel('平仓类型')
    axes[1, 0].set_ylabel('单笔盈亏 (PNL)')
    axes[1, 0].grid(axis='y')

    # 图4: 不同平仓类型的持仓时间分布
    sns.boxplot(data=df, x='exit_type', y='duration_hours', ax=axes[1, 1], order=['盈利', '策略平仓亏损', '止损亏损'])
    axes[1, 1].set_title('不同平仓类型的持仓时间分布', fontsize=16)
    axes[1, 1].set_xlabel('平仓类型')
    axes[1, 1].set_ylabel('持仓小时数')
    axes[1, 1].grid(axis='y')

    plt.tight_layout()
    plt.show()


# --- 主程序 ---
if __name__ == "__main__":
    file_path = '回测结果 (2).txt'
    trades_df = parse_backtest_log_final(file_path)

    if trades_df is not None and not trades_df.empty:
        analyze_losses_deep_dive(trades_df)
    else:
        print("未能加载或解析数据，分析终止。")